import 'dart:convert';
import 'dart:io';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:xvideos/config/config.dart';
import 'package:xvideos/hj_utils/light_model.dart';
import 'package:xvideos/hj_utils/text_util.dart';
import 'package:xvideos/hj_utils/utils.dart';
import 'package:xvideos/tools_base/debug_log.dart';
import 'package:xvideos/tools_base/net/base_resp_bean.dart';
import 'package:xvideos/tools_base/net/code.dart';
import 'package:xvideos/tools_base/net/http_response_interceptor.dart';
import 'package:xvideos/tools_base/net/net_manager.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:uuid/uuid.dart';

final httpManager = _HttpManagerImp();

enum HttpMethod {
  get('GET'),
  post('POST'),
  delete('DELETE');

  final String method;
  const HttpMethod(this.method);
}

abstract class HttpManger {
  final dio = Dio();
  String _baseUrl = '';

  String get baseUrl => _baseUrl;

  /// 服务器时间校准
  int _diffTimeInSeconds = 0;
  DateTime? _serverTime;

  initDefault() {
    _addDioIns();
  }

  init(String baseUrl) {
    resetBaseUrl(baseUrl);
  }

  // 同步服务器时间
  setServerTime(String? serverTimeS) {
    if (TextUtil.isNotEmpty(serverTimeS)) {
      _serverTime = DateTime.parse(serverTimeS!);
      _diffTimeInSeconds = DateTime.now().difference(_serverTime!).inSeconds;
      debugLog("============>server diff from local in seconds:$_diffTimeInSeconds");
    }
  }

  /// get
  Future<BaseRespBean> fetchResponseByGET(
    String url, {
    Map<String, dynamic>? param,
    Options? options,
    Function(Map<String, dynamic> json)? jsonTransformation,
  });

  /// post
  Future<BaseRespBean> fetchResponseByPOST(
    String url, {
    Map<String, dynamic>? param,
    Options? options,
    Function(Map<String, dynamic> json)? jsonTransformation,
  });

  /// post

  Future<BaseRespBean> fetchResponseByDELETE(
    String url, {
    Map<String, dynamic>? param,
    Options? options,
    Function(Map<String, dynamic> json)? jsonTransformation,
  });

  Future<BaseRespBean> _requestByUrl(
    String url, {
    Map<String, dynamic>? data,
    Map<String, dynamic>? queryParameters,
    required Options options,
    Function(Map<String, dynamic> json)? jsonTransformation,
  }) async {
    late BaseRespBean resultData;
    Response? response;
    try {
      response = await dio.request(url, queryParameters: queryParameters, options: options, data: data);
    } on DioException catch (e) {
      debugLog('DioException $e');
      if (e.type == DioExceptionType.cancel) {
        resultData = BaseRespBean(Code.LOCAL_CANCEL_REQUEST, msg: '请求已经取消～，请重试', data: null);
      } else if (e.type == DioExceptionType.connectionTimeout ||
          e.type == DioExceptionType.receiveTimeout ||
          e.type == DioExceptionType.sendTimeout) {
        resultData = BaseRespBean(Code.NETWORK_TIMEOUT, msg: '网络连接超时～', data: null);
      }
      final connectivityResult = await Connectivity().checkConnectivity();
      if (connectivityResult == ConnectivityResult.none) {
        //没有网络
        resultData = BaseRespBean(Code.LOCAL_NO_NETWORK, msg: '暂无网络，请检查网络设置', data: null);
      } else {
        resultData = BaseRespBean(Code.NETWORK_ERROR, msg: '网络异常，请重新试试～', data: null);
      }
    } on SocketException catch (e) {
      resultData = BaseRespBean(Code.NETWORK_TIMEOUT, msg: '网络连接超时～', data: null);
      debugLog('SocketException $e');
    } on HttpException catch (e) {
      resultData = BaseRespBean(Code.NETWORK_ERROR, msg: '网络异常请稍后再试～', data: null);
      debugLog('HttpException $e');
    } on FormatException catch (e) {
      debugLog('FormatException $e');
      resultData = BaseRespBean(Code.NETWORK_ERROR, msg: '网络异常请稍后再试～', data: null);
    } catch (e) {
      resultData = BaseRespBean(Code.NETWORK_ERROR, msg: '网络异常请稍后再试～', data: null);
      debugLog(e);
    }

    if (response == null) {
      return resultData;
    } else if (response.data is BaseRespBean) {
      resultData = response.data;
    } else if (response.data is Map<String, dynamic>) {
      resultData = BaseRespBean.fromJson(response.data);
    }
    if (resultData.isSuccess) {
      if (jsonTransformation != null) {
        final data_ = resultData.data;
        if (data_ is Map) {
          try {
            resultData.data = jsonTransformation.call(Map<String, dynamic>.from(data_));
          } catch (e) {
            print('jsonTransformation error $e');
            resultData.data = null;
          }
        }
      }
    }
    return resultData;
  }

  // dio 添加拦截
  _addDioIns() {
    dio.interceptors.add(HttpResponseInterceptor());
  }

  // 重制地址
  resetBaseUrl(String baseUrl) {
    _baseUrl = baseUrl;
    final options = BaseOptions(
      connectTimeout: const Duration(seconds: 15),
      receiveTimeout: const Duration(seconds: 15),
      validateStatus: (int? status) => (status ?? 600) < 600,
      baseUrl: baseUrl,
    );

    options.headers[HttpHeaders.acceptEncodingHeader] = "*";

    dio.options = options;

    var adapter = DefaultHttpClientAdapter();

    adapter.onHttpClientCreate = (client) {
      client.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
      return client;
    };
    dio.httpClientAdapter = adapter;
  }

  // 获取统一的请求头
  Future<Options> generateRequestOption(String apiUrl, {Options? options, required HttpMethod method}) async {
    options ??= Options(method: method.method);
    options.headers ??= {};
    final token = await netManager.getToken(); //(await lightKV.getString("_key_net_token")) ?? '';
    if (token.isNotEmpty == true) {
      options.headers?["Authorization"] = token;
    }
    if (options.method == "GET") {
      //options.headers["Content-Type"] = "application/x-www-form-urlencoded";
    } else if (options.method == "POST") {
      options.headers?["Content-Type"] = "application/json;charset=UTF-8";
    }
    options.headers?["User-Agent"] = await _fetchUserAgent();
    options.headers?["api_version"] = "1.0.0";
    options.headers?["device"] = Platform.operatingSystem;
    Uri? baseUri = Uri.tryParse(baseUrl);
    Uri targetUri = Uri(scheme: baseUri?.scheme, host: baseUri?.host, port: baseUri?.port, path: baseUri!.path + apiUrl);
    options.headers?["x-api-key"] = await _sign(targetUri.path);

    return options;
  }

  /// 签名
  Future<String> _sign(String path) async {
    Map<String, dynamic> signObj = {};
    final timeDate = DateTime.now().add(Duration(seconds: -_diffTimeInSeconds));
    int timestamp = timeDate.toUtc().millisecondsSinceEpoch ~/ 1000;
    signObj['nonce'] = const Uuid().v4();
    signObj['path'] = path;
    signObj['timestamp'] = timestamp.toString();
    signObj['token'] = await netManager.getToken();
    signObj['userAgent'] = await netManager.userAgent();
    var key = utf8.encode(Config.ANTI_REPLAY_ATTACK_KEY);
    var bytes = utf8.encode(jsonEncode(signObj).toString());
    var sha1Encrypt = Hmac(sha1, key);
    var digest = sha1Encrypt.convert(bytes);
    return 'timestamp=$timestamp;sign=${digest.toString()};nonce=${signObj['nonce']}';
  }

  // 请求ua
  Future<String> _fetchUserAgent({String? deviceId}) async {
    String oldUa = (await lightKV.getString("_key_user_agent")) ?? '';
    if (TextUtil.isEmpty(deviceId) && TextUtil.isNotEmpty(oldUa)) {
      return oldUa;
    }
    String newUa = await _genUserAgent(deviceId);
    if (TextUtil.isNotEmpty(newUa)) lightKV.setString("_key_user_agent", newUa);
    return newUa;
  }

  Future<String> _genUserAgent(String? deviceId) async {
    if (TextUtil.isEmpty(deviceId)) {
      deviceId = await getDeviceOaid();
    }
    String devType = await getDevType();
    String sysType = Platform.operatingSystem;
    String ver = Config.innerVersion;
    String buildID = await getFixedPkgName();
    String ua = Uri.encodeComponent(
        "DevID=$deviceId;DevType=$devType;SysType=$sysType;Ver=$ver;BuildID=$buildID;Mac=${Config.macAddress};GlobalDevID=${Config.imei}");
    return ua;
  }

  /// 获取修正后的pkgName
  Future<String> getFixedPkgName() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    if (Platform.isIOS) {
      return Config.IOS_TF_BUNDLE_ID;
    } else {
      return packageInfo.packageName;
    }
  }
}

class _HttpManagerImp extends HttpManger {
  @override
  Future<BaseRespBean> fetchResponseByDELETE(String url,
      {Map<String, dynamic>? param, Options? options, Function(Map<String, dynamic> json)? jsonTransformation}) async {
    final options = await generateRequestOption(url, method: HttpMethod.delete);
    return await _requestByUrl(url, options: options, data: param, jsonTransformation: jsonTransformation);
  }

  @override
  Future<BaseRespBean> fetchResponseByGET(
    String url, {
    Map<String, dynamic>? param,
    Options? options,
    Function(Map<String, dynamic> json)? jsonTransformation,
  }) async {
    final options = await generateRequestOption(url, method: HttpMethod.get);
    return await _requestByUrl(url, options: options, queryParameters: param, jsonTransformation: jsonTransformation);
  }

  @override
  Future<BaseRespBean> fetchResponseByPOST(
    String url, {
    Map<String, dynamic>? param,
    Options? options,
    Function(Map<String, dynamic> json)? jsonTransformation,
  }) async {
    final options = await generateRequestOption(url, method: HttpMethod.post);
    return await _requestByUrl(url, options: options, data: param, jsonTransformation: jsonTransformation);
  }
}
